/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.icee.myth.manager.server;

import com.icee.myth.common.encoder.Type2BytesLengthFieldProtobufEncoder;
import com.icee.myth.common.message.serverMessage.Message;
import com.icee.myth.manager.DeamonToManagerServer;
import com.icee.myth.manager.Manager;
import com.icee.myth.manager.ManagerToServerClient;
import com.icee.myth.manager.channelHandler.ManagerToClusterHandler;
import com.icee.myth.manager.config.ServerConfig;
import com.icee.myth.manager.db.DbHandler;
import com.icee.myth.manager.message.serverMessage.GmMessage;
import com.icee.myth.manager.server.state.*;
import com.icee.myth.protobuf.builder.ConsoleToManagerBuilder;
import com.icee.myth.protobuf.builder.DeamonToManagerBuilder;
import com.icee.myth.utils.Consts;
import com.icee.myth.utils.LogConsts;
import com.icee.myth.utils.MLogger;
import com.icee.myth.utils.StackTraceUtil;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * manager服务器所管理的游戏服务器
 *
 * @author liuxianke
 */
public class Server {

    public ServerConfig serverConfig;
    public ServerState state;
    public ManagerToServerClient managerToServerClient;
    private TopLevelPlayer[] topLevelPlayers;
    private long topPlayerExpireTime;
    private boolean bTest = true;

    public Server(ServerConfig serverConfig, ServerState state) {
        this.serverConfig = serverConfig;
        this.state = state;
        topLevelPlayers = new TopLevelPlayer[Consts.TOPLEVELNUM];
        managerToServerClient = new ManagerToServerClient(this);
        LengthFieldBasedFrameDecoder lengthFieldBasedFrameDecoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 2, 4, 0, 0);
        Type2BytesLengthFieldProtobufEncoder type2BytesLengthFieldProtobufEncoder = new Type2BytesLengthFieldProtobufEncoder();
        ManagerToClusterHandler toClusterHandler = new ManagerToClusterHandler(serverConfig.id);
        managerToServerClient.init(serverConfig.host, serverConfig.managerPort, lengthFieldBasedFrameDecoder, type2BytesLengthFieldProtobufEncoder, toClusterHandler);
    }

    public int getId() {
        return serverConfig.id;
    }

    private boolean checkReferenceServerDevices(ServerConfig serverConfig) {
        if (Manager.INSTANCE.getDeamonChannelContext(serverConfig.host) == null) {
            return false;
        }

        return true;
    }

    private void zip(ZipOutputStream out, File f, String base) throws Exception {

        System.out.println("Zipping  " + f.getName());

        if (f.exists()) {
            if (f.isDirectory()) {
                if (f.getName().compareTo(".svn") != 0) {
                    File[] fl = f.listFiles();

                    out.putNextEntry(new ZipEntry(base + "/"));

                    base = base.length() == 0 ? "" : base + "/";

                    for (int i = 0; i < fl.length; i++) {
                        String name = fl[i].getName();
                        if (null != name && !"".equals(name)) {
                            // 递归调用
                            zip(out, fl[i], base + fl[i].getName());
                        }
                    }
                }
            } else {
                if (f.getName().endsWith(".json") || f.getName().endsWith(".bin")) {
                    out.putNextEntry(new ZipEntry(base));

                    FileInputStream in = new FileInputStream(f);

                    byte[] fileData = new byte[2048];
                    int readNum = 0;
                    while ((readNum = in.read(fileData, 0, 2048)) != -1) {
                        out.write(fileData, 0, readNum);
                    }

                    in.close();
                }
            }
        } else {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, f.getName() + " not exist");
        }
    }

    private boolean sendConfigFiles(ServerConfig serverConfig) {
        try {
            // 传送服务对应的配置文件给Server
            // 将config目录打成zip包
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
            zip(zipOutputStream, new File("config/"), "");
            zipOutputStream.close();
            byte[] buf = byteArrayOutputStream.toByteArray();
            DeamonToManagerServer.getInstance().getChannelContext(serverConfig.host).write(DeamonToManagerBuilder.buildServerConfigData(serverConfig.id, buf));
            byteArrayOutputStream.close();
            return true;
        } catch (Exception ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            return false;
        }
    }

    private void startServer(ServerConfig serverConfig) {
        // 启动Server
        // -id 0 -host 172.17.36.66 -port 2000 -dbHost 172.17.36.66 -managerPort 6000 -staticScenes 10001
        String paramStr = " -id " + serverConfig.id;
        paramStr += " -regionId " + serverConfig.regionId;
        paramStr += " -port " + serverConfig.externalPort;
        paramStr += " -internalHost " + serverConfig.host;
        paramStr += " -dbHost " + serverConfig.dbHost;
        paramStr += " -dbName " + serverConfig.dbName;
        paramStr += " -logDBHost " + serverConfig.logDBHost;
        paramStr += " -logDBNamePrefix " + serverConfig.logDBNamePrefix;
        paramStr += " -managerPort " + serverConfig.managerPort;
        paramStr += " -rpcPort " + serverConfig.rpcPort;
        paramStr += " -billGetAssetUrl " + serverConfig.billServerGetAssetAddress;
        paramStr += " -billTransactionUrl " + serverConfig.billServerTransactionAddress;
        paramStr += " -couponApplyUrl " + serverConfig.couponServerApplyAddress;
        paramStr += " -billApiKey " + serverConfig.billApiKey;
        paramStr += " -billApiSecret " + serverConfig.billApiSecret;

        DateFormat format = new SimpleDateFormat("yyyy-MM-dd|HH:mm:ss");
        paramStr += " -openTime " + format.format(serverConfig.openTime);

        paramStr += " -loginCheckUrl http://" + Manager.INSTANCE.webHost + ":" + Manager.INSTANCE.webPort + "/session/check";

        Manager.INSTANCE.getDeamonChannelContext(serverConfig.host).write(DeamonToManagerBuilder.buildStartServer(serverConfig.jvmOption, paramStr));
    }

    public boolean start(boolean needSendConfig) {
        // 检查所有相关Deamon设备是否准备好
        if (checkReferenceServerDevices(serverConfig)) {
            // 向相关的Deamon发送配置文件
            if (!needSendConfig || sendConfigFiles(serverConfig)) {
                startServer(serverConfig);
            } else {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Can't startOnlyOnce server[" + serverConfig.id + "] because send config file error.");
                return false;
            }
        } else {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Can't startOnlyOnce server[" + serverConfig.id + "] because not all devices ready.");
            return false;
        }

        return true;
    }

    public synchronized void shutdown() {
        managerToServerClient.shutdown();

        // 进入关闭中状态
        state = ClosingServerState.INSTANCE;
    }

    public void tryConnectServer() {
        managerToServerClient.connectToServer();
    }

    public void flush() {
        managerToServerClient.flush();
    }

    public void handleMessage(Message message) {
        // 忽略结束状态后的消息
        switch (message.getType()) {
            case ALL_HEARTBEAT:
            case MANAGER_CLUSTER_HEARTBEAT: {
                managerToServerClient.handleMessage(message);
                break;
            }
            case MANAGER_FORCE_SET_SERVER_SHUTDOWN_STATUS: {
                if (state != RunningServerState.INSTANCE) {
                    state = ShutdownServerState.INSTANCE;

                    // 将结果发回Console
                    Manager.INSTANCE.broadcastToConsole(ConsoleToManagerBuilder.buildForceSetServerShutdownStatusOK(getId()));
                } else {
                    // 将结果发回Console
                    Manager.INSTANCE.broadcastToConsole(ConsoleToManagerBuilder.buildForceSetServerShutdownStatusError(getId()));
                }
                break;
            }
            default: {
                state.handleMessage(this, message);
            }
        }
    }

    public void gm(GmMessage gmMessage) {
        managerToServerClient.gm(gmMessage);
    }

    public synchronized void setTestFlag(boolean flag) {
        bTest = flag;
    }

    public synchronized int getServerStatusFlag() {
        if (state == UnknownServerState.INSTANCE) {
            return Consts.SERVER_STATUS_UNKNOWN;
        } else if (state == BootingServerState.INSTANCE) {
            return Consts.SERVER_STATUS_BOOTING;
        } else if (state == RunningServerState.INSTANCE) {
            if (bTest) {
                return Consts.SERVER_STATUS_TESTING;
            }
            return Consts.SERVER_STATUS_RUNNING;
        } else if (state == ClosingServerState.INSTANCE) {
            return Consts.SERVER_STATUS_CLOSING;
        } else if (state == ShutdownServerState.INSTANCE) {
            return Consts.SERVER_STATUS_SHUTDOWN;
        } else if (state == AbnormalRunningServerState.INSTANCE) {
            return Consts.SERVER_STATUS_ABNORMALRUNNING;
        } else if (state == DummyServerState.INSTANCE) {
            return Consts.SERVER_STATUS_DUMMY;
        }
        return Consts.SERVER_STATUS_UNKNOWN;
    }

    public synchronized String getServerStatus() {
        if (state == RunningServerState.INSTANCE) {
            if (bTest) {
                return "testing";
            }
            return "running";
        } else if (state == BootingServerState.INSTANCE) {
            return "booting";
        } else if (state == ClosingServerState.INSTANCE) {
            return "closing";
        } else if (state == ShutdownServerState.INSTANCE) {
            return "shutdown";
        } else if (state == AbnormalRunningServerState.INSTANCE) {
            return "abnormal running";
        } else if (state == DummyServerState.INSTANCE) {
            return "dummy";
        } else {
            return "unknown";
        }
    }

    public String getName() {
        return serverConfig.name;
    }

    public boolean getTop10ByLevel(StringBuffer content) {
        if (topPlayerExpireTime < System.currentTimeMillis()) {
            DbHandler dbHandler = new DbHandler(serverConfig.dbHost, serverConfig.dbName);
            dbHandler.getTop10ByLevel(topLevelPlayers);
            dbHandler.close();
            topPlayerExpireTime = System.currentTimeMillis() + Consts.LEVELRANKPERIOD;
        }
        content.append("[");
        for (int i = 0; i < topLevelPlayers.length; i++) {
            TopLevelPlayer topLevelPlayer = topLevelPlayers[i];
            if (topLevelPlayer != null) {
                if (i != 0) {
                    content.append(",");
                }
                content.append("{\"name\":\"").append(topLevelPlayer.name).append("\",\"level\":").append(topLevelPlayer.level).append(",\"job\":").append(topLevelPlayer.job).append("}");
            }
        }
        content.append("]");
        return true;
    }

    public DBCharInfo getCharInfo(int cid) {
        DbHandler dbHandler = new DbHandler(serverConfig.dbHost, serverConfig.dbName);
        DBCharInfo charInfo = dbHandler.getUserDetailInfo(cid);
        dbHandler.close();
        return charInfo;
    }
}
